//
//  ViewExtension.swift
//  SwiftLearning
//
//  Created by 黄龙 on 2023/12/19.
//

import Foundation
import UIKit

extension UIView{
//MARK: - 任意圆角及阴影
    //任意角的圆角
    //使用方法eg(左侧圆角):view.easyRoundRect(corner: [.topLeft,.bottomLeft], radii: CGSize(width: 10,height: 15))
    func easyRoundRect(corner: UIRectCorner, radii: CGSize){
        let maskPath = UIBezierPath.init(roundedRect: bounds, byRoundingCorners: corner, cornerRadii: radii)
        let maskLayer = CAShapeLayer.init()
        maskLayer.frame = bounds
        maskLayer.path = maskPath.cgPath
        self.layer.mask = maskLayer
    }
    
//设置阴影
    func setShadow(shadowColor:UIColor,offset:CGSize,opacity:Float,radius:CGFloat){
        self.layer.shadowColor = shadowColor.cgColor
        self.layer.shadowOpacity = opacity //透明度
        self.layer.cornerRadius = radius
        self.layer.shadowOffset = offset
    }
}

extension UIView{
    //MARK: - 虚线及(圆角)虚线框
//给指定View添加一个指定大小的矩形虚线框
    func addDashdeBorderLayer(lineFrame:CGRect,color:UIColor,lineWidth lWidth:CGFloat,lineDashPattern: [NSNumber]?=[3,2],cornerRadius:CGFloat=0,fillColor:CGColor=UIColor.clear.cgColor)->CAShapeLayer{
        let shapeLayer = CAShapeLayer()
        shapeLayer.frame = lineFrame
        shapeLayer.fillColor = fillColor
        shapeLayer.strokeColor = color.cgColor
        shapeLayer.lineWidth = lWidth //边框线条粗细
        shapeLayer.lineJoin = CAShapeLayerLineJoin.round
        shapeLayer.lineDashPattern = lineDashPattern //[lineLength,SpaceLength]
        //虚线(有个bug，设置成虚线lineDashPattern[4,3]时，对其进行缩放、旋转等基础动时，会莫名妙其妙的位置莫名其妙地多出一条很细的线段),而一去掉虚线形态，就不会有，奇怪！！
        //lineDashPattern设置成其它的诸如[1,3],[4,8]等等时，动画又不会有莫名的线段
        
        if lWidth == lineFrame.width{
//绘制单条竖线
            let path = CGMutablePath()
            path.move(to: CGPoint(x: 0, y: 0))
            path.addLine(to: CGPoint(x: 0, y: self.bounds.height))
            shapeLayer.path = path
            
        }else if lWidth == lineFrame.height{
//绘制单条横线
            let path = CGMutablePath()
            path.move(to: CGPoint(x: 0, y: 0))
            path.addLine(to: CGPoint(x: self.bounds.width, y: 0))
            shapeLayer.path = path
        }else{
//绘制整个view的边框
            if cornerRadius>0 {
                let path = UIBezierPath(roundedRect: CGRect(origin: CGPointZero, size: lineFrame.size), cornerRadius: 10)
                shapeLayer.path = path.cgPath
            }else{
               let path = UIBezierPath(rect: CGRect(origin: CGPointZero, size: lineFrame.size))
                shapeLayer.path = path.cgPath
            }
        }
//        self.layer.addSublayer(shapeLayer) //用此方法，如果是UIButton,会覆盖掉文字标题
        self.layer.insertSublayer(shapeLayer, at: 0) //但即使是insert到最底层，也会影响UIButton的image显示(可以用bringSubviewToFront强制将imageView置前)
        
        return shapeLayer
    }
    
//添加圆(弧)虚线框
    func addCircleDashdeBorderLayer(center:CGPoint,raduis:CGFloat,color:UIColor,lineWidth lWidth:CGFloat,startAngle:CGFloat=0,endAngle:CGFloat=2*Double.pi, lineDashPattern: [NSNumber]?=[3,2],fillColor:CGColor=UIColor.clear.cgColor)->CAShapeLayer{
        let shapeLayer = CAShapeLayer()
        shapeLayer.fillColor = fillColor
        shapeLayer.strokeColor = color.cgColor
        shapeLayer.lineWidth = lWidth //边框线条粗细
        shapeLayer.lineJoin = CAShapeLayerLineJoin.round
        shapeLayer.lineDashPattern = lineDashPattern //[lineLength,SpaceLength]
        let path = UIBezierPath(arcCenter: center, radius: raduis, startAngle: startAngle, endAngle: endAngle, clockwise: true)
        shapeLayer.path = path.cgPath
//        self.layer.addSublayer(shapeLayer)
        self.layer.insertSublayer(shapeLayer, at: 0)
        return shapeLayer
    }
    
}

extension UIView{
//MARK: - 镜头动画(打开/关闭镜头的动画)
    func animateCircular(withDuration duration: TimeInterval, center: CGPoint, revert: Bool = false, animations: () -> Void, completion: ((Bool) -> Void)? = nil) {
//镜头开关动画缩放，revert=true,表镜头关闭(缩小)，=false表示镜头开启(放大)
        if let snapshot = self.snapshotView(afterScreenUpdates: false){
            snapshot.frame = bounds
            self.addSubview(snapshot)
            
            let center = convert(center, to: snapshot)
            let radius: CGFloat = {
                let x = max(center.x, frame.width - center.x)
                let y = max(center.y, frame.height - center.y)
                return sqrt(x * x + y * y)
            }()
            var animation : CircularRevealAnimator
            if !revert {
                animation = CircularRevealAnimator(layer: snapshot.layer, center: center, startRadius: 0, endRadius: radius, invert: true)
            } else {
                animation = CircularRevealAnimator(layer: snapshot.layer, center: center, startRadius: radius, endRadius: 0, invert: false)
            }
            animation.duration = duration
            animation.completion = {
                snapshot.removeFromSuperview()
                completion?(true)
            }
            animation.start()
            animations()
        }
    }
}


extension UIView{
//MARK: - 将指定View截屏成View及转成UIImage
    //截取View屏幕并转为View,类似copyView（注意，如View在后台时，会返回nil）
        func snapshot2View()->UIView?{
            return self.snapshotView(afterScreenUpdates: false)
        }
        
    //截取整个view的图像并转为image
        func snapshot2Image(_ scale:CGFloat? = nil) -> UIImage? {
            let okScale = (scale != nil) ? scale! : UIScreen.main.scale //UIScreen.main.scale 1=1x,2=2x,3=3x
            UIGraphicsBeginImageContextWithOptions(self.frame.size,false, okScale)
            self.layer.render(in:UIGraphicsGetCurrentContext()!) //渲染到上下文
            let image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return image
        }
        
    //截取view的某个区域(shotRect)图像并转为image （如将指View切割成一个个碎片Image）
        func snapshot2Image(with shotRect:CGRect,afterScreenUpdates flag:Bool=true) -> UIImage? {
            UIGraphicsBeginImageContextWithOptions(shotRect.size,false, UIScreen.main.scale)
            let _ = UIGraphicsGetImageFromCurrentImageContext()
            self.drawHierarchy(in: CGRect(origin: CGPoint(x: -shotRect.origin.x, y: -shotRect.origin.y), size: self.bounds.size), afterScreenUpdates: flag)
    //注意drawHierarchy的Rect设置:(x: -shotRect.origin.x, y: -shotRect.origin.y)
    //afterScreenUpdates如果是true, 则是addSubview之后,  如果是false,则是addSubview之前
            let image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return image
        }
}

extension UIView{
//MARK: - 将view分裂成指定大小的碎片，然后下跌UIView.animate位移动画
    func animateToCrush(crushSize:CGSize,xSmall:CGFloat = -10.0,xBig:CGFloat=10.0, duration:TimeInterval=0.5,delayTime:TimeInterval=0.25) {
        let tmpView = UIView(frame: self.frame) //创建一个相同位置的在前面放置撕碎的碎片
        self.superview?.insertSubview(tmpView, aboveSubview: self)
        tmpView.clipsToBounds = true
        
        var snapshots:[UIImageView] = []
        for x in stride(from: CGFloat(0), through: self.bounds.width, by: crushSize.width) {
            for y in stride(from: CGFloat(0), to: self.bounds.height, by: crushSize.height) {
                let snapshotRegion = CGRect(origin: CGPoint(x: x, y: y), size: crushSize)
                if let strideImage = self.snapshot2Image(with: snapshotRegion){
                    //撕成小碎片并绘制到临时tmpImgView上,如果碎片过多，则造成崩溃;不适合将大的图像，撕成很小的区块
                    let tmpImgView = UIImageView(frame: snapshotRegion)
                    tmpView.addSubview(tmpImgView)
                    tmpImgView.image = strideImage
//                    tmpImgView.layer.borderWidth = 0.25 //test.标识是否碎裂成功
//                    tmpImgView.layer.borderColor = UIColor.yellow.cgColor
                    snapshots.append(tmpImgView)
                }
            }
        }
//先将本身移除
        self.removeFromSuperview()
//然后将碎片下跌并移除
        UIView.animate(
            withDuration:
                0.15,
            delay: 0,
            options: UIView.AnimationOptions.curveLinear,
            animations: {
                for perView in snapshots {
                    let xOffset = SwiftFunc.randomFloatBetween(xSmall , and: xBig)
                    let yOffset = SwiftFunc.randomFloatBetween(xSmall , and: xBig)
                    perView.frame = perView.frame.offsetBy(dx: CGFloat(xOffset), dy: CGFloat(yOffset)) //先打碎
                }
            },
            completion: { _ in
                UIView.animate(withDuration: duration, delay: delayTime) {
                    for perView in snapshots {
                        let xOffset = SwiftFunc.randomFloatBetween(xSmall , and: xBig)
                        let yOffset = SwiftFunc.randomFloatBetween(tmpView.frame.height, and: tmpView.frame.height * 1.2)
                        perView.frame = perView.frame.offsetBy(dx: CGFloat(xOffset), dy: CGFloat(yOffset)) //然后下跌
                    }
                } completion: { _ in
                    for perView in snapshots {
                        perView.removeFromSuperview()
                    }
                    tmpView.removeFromSuperview() //移除临时view
                }
                
            }
        )
    }
}

extension UIView{
//MARK: - 设置渐变色view
//有3种渐变效果：LinearGradient线性梯度、AngularGradient(.conic)角梯度渐变(角度梯度也称为“圆锥”梯度)、RadialGradient(.radial)径向梯度(圆的半径方向)
    func gradientLayer(With colorsSet:[CGColor],locationSet:[NSNumber]?=nil,startPoint:CGPoint?=nil,endPoint:CGPoint?=nil)->CAGradientLayer?{
        //创建CAGradientLayer对象
         let gradientLayer = CAGradientLayer()
        //设置初始渐变色
        gradientLayer.colors = colorsSet
        
//locations: the location of each gradient stop as a value in the range [0,1]
        //每种颜色所在的位置,即渐变停止位置，指定为0到1之间的值.值必须单调递增.
        if let locations = locationSet{
            gradientLayer.locations = locations //default=[0.0, 1.0]
        }
        if let startPoint = startPoint{
            gradientLayer.startPoint = startPoint //default(0.5,0) 上中
        }
        if let endPoint = endPoint{
            gradientLayer.endPoint = endPoint //default (0.5,1) 下中
        }
        gradientLayer.frame = self.bounds
//        self.layer.addSublayer(gradientLayer)
        self.layer.insertSublayer(gradientLayer, at: 0)
        //以免UIButton之类的，add之后会遮盖title,另外CAGradientLayer.insertLayer后不会像CAShapeLayer那样会遮盖button的image
        return gradientLayer
    }
    
    func radialGradientLayer(with colorsSet:[CGColor],startPos:CGPoint,endPos:CGPoint)->CAGradientLayer?{
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = self.bounds
        gradientLayer.type = .radial
       //设置初始渐变色
        gradientLayer.colors = colorsSet
        //在线性渐变中startPoint和endPoint是可选参数，而在径向(radial)和圆锥(conic)中则是必须参数
        gradientLayer.startPoint = startPos //在径向中，startPoint为圆的中心点(0.5,0.5)为中间点，endPoint为圆的远点(1.0,1.0)为远点
        gradientLayer.endPoint = endPos
        self.layer.insertSublayer(gradientLayer, at: 0)
        return gradientLayer
    }
    
    func angularGradientLayer(with colorsSet:[CGColor],startPos:CGPoint,endPos:CGPoint)->CAGradientLayer?{
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = self.bounds
        gradientLayer.type = .conic
       //设置初始渐变色
        gradientLayer.colors = colorsSet
        //在线性渐变中startPoint和endPoint是可选参数，而在径向(radial)和圆锥(conic)中则是必须参数
        gradientLayer.startPoint = startPos //在圆锥中，startPoint为圆锥的中心点，endPoint为第一种颜色和最后一种颜色合并的点
        gradientLayer.endPoint = endPos
        self.layer.insertSublayer(gradientLayer, at: 0)
        return gradientLayer
    }
}

extension UIView{
//在本View上显示一个Toast,默认2s
    func showToast(_ msg:String,titleColor:UIColor = UIColor.label,bgColor:UIColor?=nil,isBottom:Bool=true,iSpace:CGFloat=100.0, showTime: Double = 2.0){
//iSpace为离底部或顶部的距离
        let lastToView = self.viewWithTag(1758)
        if lastToView != nil{
            NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(autoRemoveToast(_:)), object: "1")
            lastToView?.removeFromSuperview()
        }else{
            let lastView = self.viewWithTag(1759)
            if lastView != nil{
                NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(autoRemoveToast(_:)), object: "0")
                lastView?.removeFromSuperview()
            }
        }
        
        let iPendding = 15.0
        let toastWidth = self.bounds.width - iPendding*2.0
        let toastHeight = 50.0
        let toastView = UIView()
        self.addSubview(toastView)
        toastView.tag = isBottom ? 1758 : 1759
        if let backColor = bgColor{
            toastView.backgroundColor = backColor
        }else{
            toastView.backgroundColor = UIColor.systemBackground.withAlphaComponent(0.75)
        }
        
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: toastWidth , height: toastHeight))
        toastView.addSubview(label)
        label.numberOfLines = 0
        label.adjustsFontSizeToFitWidth = true
        label.textColor = titleColor
        label.font = .systemFont(ofSize: 16)
        label.text = msg
        label.textAlignment = .center
        label.sizeToFit() //重新得label的宽高
        
        let txtSize = label.bounds.size
        
        let v_Height = txtSize.height + 2*iPendding
        if isBottom{
            //toast显示在底部：初始y=self.bounds.height + 10，然后动画向上恢复底部位置
            if self.bounds.width > txtSize.width + 2*iPendding{
                let v_Width = txtSize.width + 2*iPendding
                let iLeft = v_Width/2 - txtSize.width/2
                toastView.frame = CGRect(x: iLeft, y: self.bounds.height + iSpace, width: v_Width, height: v_Height)
                label.frame = CGRect(origin: CGPoint(x: iPendding, y: iPendding), size: txtSize)
            }else{
                toastView.frame = CGRect(x: 0, y: self.bounds.height + iSpace, width: self.bounds.width, height: v_Height)
                let iLeft = self.bounds.width/2 - txtSize.width/2
                label.frame = CGRect(origin: CGPoint(x: iLeft, y: iPendding), size: txtSize)
            }
        }else{
            //toast显示在顶部: 初始位置-height，然后动画到顶部位置
            if self.bounds.width > txtSize.width + 2*iPendding{
                let v_Width = txtSize.width + 2*iPendding
                let iLeft = v_Width/2 - txtSize.width/2
                toastView.frame = CGRect(x: iLeft, y: -iSpace - v_Height, width: v_Width, height: v_Height)
                label.frame = CGRect(origin: CGPoint(x: iPendding, y: iPendding), size: txtSize)
            }else{
                toastView.frame = CGRect(x: 0, y: -iSpace - v_Height, width: self.bounds.width, height: v_Height)
                let iLeft = self.bounds.width/2 - txtSize.width/2
                label.frame = CGRect(origin: CGPoint(x: iLeft, y: iPendding), size: txtSize)
            }
        }
        let radius = (toastView.bounds.height <= 60) ? toastView.bounds.height/2 : 10.0
        toastView.setShadow(shadowColor: titleColor, offset: CGSize(width: 3, height: 3), opacity: 0.75, radius: radius)
        
//usingSpringWithDamping传入数值为0.0~1.0,数值越大,弹性越不明显
//initialSpringVelocity数值越大，初始速度越大
        let V_HEIGHT = toastView.bounds.height
        let V_WIDTH = toastView.bounds.width
        let V_LEFT = toastView.frame.origin.x
        UIView.animate(withDuration: 0.5, delay:0.25, usingSpringWithDamping: 0.4, initialSpringVelocity: 25, options: [.curveEaseInOut]) {
            if isBottom{
                toastView.frame = CGRect(x: V_LEFT, y: self.bounds.height - V_HEIGHT - iSpace, width: V_WIDTH, height: V_HEIGHT)
            }else{
                toastView.frame = CGRect(x: V_LEFT, y: iSpace, width: V_WIDTH, height: V_HEIGHT)
            }
        }
        
        self.perform(#selector(autoRemoveToast(_:)), with: isBottom ? "1" : "0", afterDelay: showTime)
    }
    
    @objc func autoRemoveToast(_ isBottom:String){
        if let toastView = self.viewWithTag(("1" ==  isBottom) ? 1758 : 1759) {
            let V_HEIGHT = toastView.bounds.height
            let V_WIDTH = toastView.bounds.width
            let V_LEFT = toastView.frame.origin.x
            UIView.animate(withDuration: 0.25, delay: 0,options: [.curveLinear]) {
                if "1" == isBottom{//底部位置
                    toastView.frame = CGRect(x: V_LEFT, y: self.bounds.height + 15 , width: V_WIDTH, height: V_HEIGHT)
                }else{
                    toastView.frame = CGRect(x: V_LEFT, y: -15 - V_HEIGHT , width: V_WIDTH, height: V_HEIGHT)
                }
            } completion: { okflag in
                toastView.removeFromSuperview()
            }
        }
    }
}
